package com.biboheart.adapter.adapter.impl;

import com.biboheart.adapter.adapter.Converter;
import com.biboheart.adapter.compiler.ast.Node;
import com.biboheart.adapter.support.enums.StructureTypeEnum;
import com.biboheart.adapter.support.exception.ConvertException;
import com.biboheart.adapter.support.utils.NodeUtils;
import com.biboheart.adapter.support.utils.ObjectTypeUtils;

import java.util.*;

public class DefaultConverter implements Converter {
    @Override
    public <T> T conversion(Object input, String describe, Class<T> clazz) throws ConvertException {
        if (null == describe || describe.isEmpty()) {
            return ObjectTypeUtils.convertType(input, clazz);
        }
        Structure structure = new Structure(describe);
        Structure.StructureNode structureNode = structure.getStructureNode();
        if (null == structureNode) {
            throw new ConvertException("解析结构出错, 结果描述内容不合法");
        }
        if (null == structureNode.getNode() && (null == structureNode.getChildren() || structureNode.getChildren().isEmpty())) {
            throw new ConvertException("解析结构出错, 结果描述内容不合法");
        }
        Source source = new Source(input);
        Target<T> target = new Target<>();
        Map<String, Integer> sourceListIndex = new HashMap<>();
        setValue(source, target, sourceListIndex, structureNode, "");
        return target.result(clazz);
    }

    private void setValue(Source source, Target<?> target, Map<String, Integer> sourceListIndex, Structure.StructureNode structureNode, String targetPath) {
        if (null == structureNode) {
            return;
        }
        if (null == targetPath) {
            targetPath = "";
        }
        StructureTypeEnum nodeType = structureNode.getType();
        Node node = structureNode.getNode();
        if (null == nodeType || StructureTypeEnum.STRUCTURE_TYPE_PRIMITIVE.equals(nodeType)) {
            Object value = source.getValueNode(node, sourceListIndex);
            target.value(value, targetPath, nodeType);
            return;
        }
        String sourceListPath = sourceListPath(structureNode, source);
        String sourceListContainsIndexPath = containsIndexPath(sourceListPath, sourceListIndex);
        if (StructureTypeEnum.STRUCTURE_TYPE_MAP.equals(nodeType)) {
            target.value(null, targetPath, nodeType);
            List<Structure.StructureNode> children = structureNode.getChildren();
            if (null == children || children.isEmpty()) {
                return;
            }
            for (Structure.StructureNode child : children) {
                String childName = child.getName();
                if (childName.contains(".")) {
                    childName = childName.substring(childName.lastIndexOf(".") + 1);
                }
                setValue(source, target, sourceListIndex, child, targetPath + "." + childName);
            }
            return;
        }
        Integer size = source.getListSize().get(sourceListContainsIndexPath);
        List<Structure.StructureNode> children = structureNode.getChildren();
        if (Integer.valueOf(0).equals(size)) {
            return;
        }
        target.value(null != size ? new ArrayList<>(Arrays.asList(new Object[size])) : new ArrayList<>(Arrays.asList(new Object[1])), targetPath, nodeType);
        if ((null == children || children.isEmpty()) && null == node) {
            return;
        }
        int len = null == size ? 1 : size;
        for (int i = 0; i < len; i++) {
            sourceListIndex.put(sourceListPath, i);
            if (null != children && !children.isEmpty()) {
                target.value(null, targetPath + "." + i, StructureTypeEnum.STRUCTURE_TYPE_MAP);
                for (Structure.StructureNode child : children) {
                    String childName = child.getName();
                    if (childName.contains(".")) {
                        childName = childName.substring(childName.lastIndexOf(".") + 1);
                    }
                    setValue(source, target, sourceListIndex, child, targetPath + "." + i + "." + childName);
                }
            } else {
                Object value = source.getValueNode(node, sourceListIndex);
                target.value(value, targetPath + "." + i, StructureTypeEnum.STRUCTURE_TYPE_PRIMITIVE);
            }
        }
    }

    private String containsIndexPath(String sourcePath, Map<String, Integer> sourceListIndex) {
        if (null == sourcePath) {
            return null;
        }
        if (!sourcePath.contains(".")) {
            return sourcePath;
        }
        String[] split = sourcePath.split("\\.");
        List<String> pathList = new ArrayList<>();
        List<String> keyList = new ArrayList<>();
        for (String str : split) {
            keyList.add(str);
            String key = String.join(".", keyList);
            if (key.equals(sourcePath)) {
                pathList.add(str);
                break;
            }
            pathList.add(str);
            if (!sourceListIndex.containsKey(key)) {
                continue;
            }
            pathList.add(String.valueOf(sourceListIndex.get(key)));
        }
        return String.join(".", pathList);
    }

    private String sourceListPath(Structure.StructureNode structureNode, Source source) {
        if (!StructureTypeEnum.STRUCTURE_TYPE_LIST.equals(structureNode.getType())) {
            return null;
        }
        List<String> listPathList = source.getListPathList();
        Node node = structureNode.getNode();
        List<Structure.StructureNode> children = structureNode.getChildren();
        String listPath = null;
        if (null != children && !children.isEmpty()) {
            for (Structure.StructureNode child : children) {
                Node childNode = child.getNode();
                if (null == childNode) {
                    continue;
                }
                List<String> variableList = NodeUtils.listVariable(childNode);
                if (null != variableList && !variableList.isEmpty()) {
                    for (String variable : variableList) {
                        String parent = parentListPath(variable, listPathList);
                        if (null == parent) {
                            continue;
                        }
                        if (listPathList.contains(parent)) {
                            listPath = parent;
                            break;
                        }
                    }
                }
                if (null != listPath) {
                    break;
                }
            }
            if (null != listPath) {
                return listPath;
            }
        }
        if (null == node) {
            return null;
        }
        List<String> variableList = NodeUtils.listVariable(node);
        if (null != variableList && !variableList.isEmpty()) {
            for (String variable : variableList) {
                String parent = parentListPath(variable, listPathList);
                if (null == parent) {
                    continue;
                }
                if (listPathList.contains(parent)) {
                    listPath = parent;
                    break;
                }
            }
        }
        return listPath;
    }

    private String parentListPath(String variable, List<String> listPathList) {
        if (null == variable || variable.isEmpty()) {
            return null;
        }
        if (!variable.contains(".")) {
            return "";
        }
        String[] split = variable.split("\\.");
        List<String> pathList = new ArrayList<>(Arrays.asList(split));
        if (!"".equals(split[0])) {
            pathList.add(0, "");
        }
        pathList.remove(pathList.size() - 1);
        String parentList = null;
        while (!pathList.isEmpty()) {
            String pathStr = String.join(".", pathList);
            if (listPathList.contains(pathStr)) {
                parentList = pathStr;
                break;
            }
            pathList.remove(pathList.size() - 1);
        }
        return parentList;
    }
}
